/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2019/3/31 10:29
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.task.worker;

import com.hyacinth.task.callback.TaskCallback;
import com.hyacinth.task.callback.WorkCallback;
import com.hyacinth.task.result.TaskResult;
import com.hyacinth.task.result.WorkResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Timer;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 作业执行器
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public class Worker<T> extends Timer {

    private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class);

    private String workId;

    private T workParams;

    private AtomicInteger taskCount = new AtomicInteger();

    private WorkCallback<T> workCallback;

    public Worker(T workParams, WorkCallback<T> workCallback) {
        this.workParams = workParams;
        this.workCallback = workCallback;
        this.workId =  UUID.randomUUID().toString();
        // 回调任务执行
        workCallback.workStarted(workId);
    }

    /**
     * 将任务加入的执行列表,加入延时执行
     *
     * @param task  任务
     * @param delay 延迟时间毫秒，0为不延迟
     */
    public void schedule(Tasker<T> task, long delay) {
        scheduleTask(task);
        super.schedule(task, delay);
    }

    /**
     * 将任务加入的执行列表,按指定的日期时间执行
     *
     * @param task 任务
     * @param time 制定时间点
     */
    public void schedule(Tasker<T> task, Date time) {
        scheduleTask(task);
        super.schedule(task, time);
    }

    /**
     * 重复执行任务，按固定的任务间隔，但频率可能会有一些延时
     *
     * @param task   任务
     * @param delay  延迟时间毫秒，0为不延迟
     * @param period 重复执行时间间隔，时间间隔单位毫秒
     */
    public void schedule(Tasker<T> task, long delay, long period) {
        scheduleTask(task, period);
        super.schedule(task, delay, period);
    }

    /**
     * 以固定的时间作为起点，重复执行任务，按固定的任务间隔，但频率可能会有一些延时
     *
     * @param task      任务
     * @param firstTime 开始执行任务的时间
     * @param period    重复执行时间间隔，时间间隔单位毫秒
     */
    public void schedule(Tasker<T> task, Date firstTime, long period) {
        scheduleTask(task, period);
        super.schedule(task, firstTime, period);
    }

    /**
     * 重复执行任务，按固定的任务间隔，每次执行任务的频率一致
     *
     * @param task   任务
     * @param delay  延迟时间毫秒，0为不延迟
     * @param period 重复执行时间间隔，时间间隔单位毫秒
     */
    public void scheduleAtFixedRate(Tasker<T> task, long delay, long period) {
        scheduleTask(task, period);
        super.scheduleAtFixedRate(task, delay, period);
    }

    /**
     * 以固定的时间作为起点，重复执行任务，按固定的任务间隔，每次执行任务的频率一致
     *
     * @param task      任务
     * @param firstTime 开始执行任务的时间
     * @param period    重复执行时间间隔，时间间隔单位毫秒
     */
    public void scheduleAtFixedRate(Tasker<T> task, Date firstTime, long period) {
        scheduleTask(task, period);
        super.scheduleAtFixedRate(task, firstTime, period);
    }


    /**
     * 任务执行按照固定的时间间隔
     * @param task   任务
     * @param period 时间间隔
     */
    private void scheduleTask(Tasker<T> task, long period) {
        task.setPeriod(period);
        scheduleTask(task);
    }

    /**
     * 任务调度
     * @param tasker 任务
     */
    private void scheduleTask(Tasker<T> tasker) {
        LOGGER.info("当前任务数：taskCount =" + taskCount);
        // 任务数递增
        taskCount.compareAndSet(taskCount.get(), taskCount.getAndIncrement());
        // 设置任务回调
        tasker.setTaskCallback(new TaskCallback<T>() {

            @Override
            public void taskFinished(TaskResult<T> taskResult) {
                taskResult.setTaskId(workId);
                taskResult.setTasker(workParams);
                //如果是定时重复执行的任务则不需要结束任务
                if (tasker.getPeriod() == null || tasker.getPeriod() <= 0) {
                    // 任务数递增
                    taskCount.compareAndSet(getTaskCount(), taskCount.getAndDecrement());
                }
                if (taskCount.get() <= 0) {
                    //所有的任务都已经执行完成，需要调用work回调，通知任务完成，并将数据传回给work
                    workCallback.workFinished(new WorkResult<>(workId, taskResult));
                    LOGGER.info("已完成任务, workId="+ workId);
                    // 停止作业
                    stopWork();
                }
            }

            @Override
            public void taskPartialData(TaskResult<T> taskResult) {
                workCallback.workPartialData(new WorkResult<>(workId, taskResult));
            }

            @Override
            public void taskException(Exception e, T tasker) {
                workCallback.workException(e, workParams);
            }
        });
    }

    /**
     * 停止work
     */
    public void stopWork() {
        //取消所有的任务
        super.cancel();
        //清空被取消的所有任务
        super.purge();
    }

    public String getWorkId() {
        return workId;
    }
    public void setWorkId(String workId) {
        this.workId = workId;
    }

    public int getTaskCount() {
        return taskCount.get();
    }

    public T getWorkParams() {
        return workParams;
    }

    public void setWorkParams(T workParams) {
        this.workParams = workParams;
    }
}
